/*
 * Copyright 2011 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.typography.font.tools.sfnttool;

import com.google.typography.font.sfntly.Font;
import com.google.typography.font.sfntly.FontFactory;
import com.google.typography.font.sfntly.Tag;
import com.google.typography.font.sfntly.table.core.CMapTable;
import com.google.typography.font.tools.subsetter.HintStripper;
import com.google.typography.font.tools.subsetter.RenumberingSubsetter;
import com.google.typography.font.tools.subsetter.Subsetter;

import sun.misc.BASE64Encoder;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author
 */
public class SfntTool {
    private boolean strip = false;
    private String subsetString = null;
    private boolean woff = false;
    private boolean eot = false;
    private boolean mtx = false;





    /**
     * 抽取数据
     *
     * @param value        文字
     * @param fontFile     源字体地址
     * @param outputStream 出参
     */
    public static void extract(String value, String fontFile, OutputStream outputStream) throws Exception {
        extract(new String[]{"-s", value, fontFile}, outputStream, false);
    }

    /**
     * 抽取数据
     *
     * @param value        文字
     * @param fontFile     入参字体
     * @param outputStream 出参
     */
    public static void extractToZip(String value, String fontFile, OutputStream outputStream) throws Exception {
        extract(new String[]{"-s", value, fontFile}, outputStream, true);
    }

    public static String encodeBase64File(String path) throws Exception {
        File file = new File(path);
        FileInputStream inputFile = new FileInputStream(file);
        byte[] buffer = new byte[(int) file.length()];
        inputFile.read(buffer);
        inputFile.close();
        return new BASE64Encoder().encode(buffer);
    }

    public static void mainV2(String[] args) throws Exception {
        FileOutputStream fileOutputStream = new FileOutputStream("D:\\\\tmp\\\\test002Base64.txt");

        String base64Str = encodeBase64File("D:\\tmp\\test002.ttf");

        fileOutputStream.write(base64Str.getBytes("UTF-8"));

        fileOutputStream.flush();
        fileOutputStream.close();
    }

    public static void extract(String[] args, OutputStream outputStream, boolean zipFlag) throws IOException {
        SfntTool tool = new SfntTool();
        File fontFile = null;
        File outputFile = null;
        boolean bench = false;
        int nIters = 1;

        for (int i = 0; i < args.length; i++) {
            String option = null;
            if (args[i].charAt(0) == '-') {
                option = args[i].substring(1);
            }

            if (option != null) {
                if (option.equals("help") || option.equals("?")) {
//                    printUsage();
                    return;
                } else if (option.equals("b") || option.equals("bench")) {
                    nIters = 10000;
                } else if (option.equals("h") || option.equals("hints")) {
                    tool.strip = true;
                } else if (option.equals("s") || option.equals("string")) {
                    tool.subsetString = args[i + 1];
                    i++;
                } else if (option.equals("w") || option.equals("woff")) {
                    tool.woff = true;
                } else if (option.equals("e") || option.equals("eot")) {
                    tool.eot = true;
                } else if (option.equals("x") || option.equals("mtx")) {
                    tool.mtx = true;
                } else {
//                    printUsage();
                    return;
                }
            } else {
                if (fontFile == null) {
                    fontFile = new File(args[i]);
                }
            }
        }

        if (tool.woff && tool.eot) {
            return;
        }

        if (fontFile != null) {
            tool.subsetFontFile(new FileInputStream(fontFile), outputStream, nIters, zipFlag);
        }
    }


    public void subsetFontFile(InputStream fis, OutputStream outputStream, int nIters, boolean zipFlag)
            throws IOException {
        FontFactory fontFactory = FontFactory.getInstance();
        try {

            int count = 0;
            while (count == 0) {
                count = fis.available();
            }
            byte[] fontBytes = new byte[count];
            fis.read(fontBytes);

            Font[] fontArray = null;
            fontArray = fontFactory.loadFonts(fontBytes);
            Font font = fontArray[0];
            List<CMapTable.CMapId> cmapIds = new ArrayList<CMapTable.CMapId>();
            cmapIds.add(CMapTable.CMapId.WINDOWS_BMP);
            byte[] newFontData = null;
            for (int i = 0; i < nIters; i++) {
                Font newFont = font;
                if (subsetString != null) {
                    Subsetter subsetter = new RenumberingSubsetter(newFont, fontFactory);
                    subsetter.setCMaps(cmapIds, 1);
                    List<Integer> glyphs = GlyphCoverage.getGlyphCoverage(font, subsetString);
                    subsetter.setGlyphs(glyphs);
                    Set<Integer> removeTables = new HashSet<Integer>();
                    // Most of the following are valid tables, but we don't renumber them yet, so strip
                    removeTables.add(Tag.GDEF);
                    removeTables.add(Tag.GPOS);
                    removeTables.add(Tag.GSUB);
                    removeTables.add(Tag.kern);
                    removeTables.add(Tag.hdmx);
                    removeTables.add(Tag.vmtx);
                    removeTables.add(Tag.VDMX);
                    removeTables.add(Tag.LTSH);
                    removeTables.add(Tag.DSIG);
                    removeTables.add(Tag.vhea);
                    // AAT tables, not yet defined in sfntly Tag class
                    removeTables.add(Tag.intValue(new byte[]{'m', 'o', 'r', 't'}));
                    removeTables.add(Tag.intValue(new byte[]{'m', 'o', 'r', 'x'}));
                    subsetter.setRemoveTables(removeTables);
                    newFont = subsetter.subset().build();
                }
                if (strip) {
                    Subsetter hintStripper = new HintStripper(newFont, fontFactory);
                    Set<Integer> removeTables = new HashSet<Integer>();
                    removeTables.add(Tag.fpgm);
                    removeTables.add(Tag.prep);
                    removeTables.add(Tag.cvt);
                    removeTables.add(Tag.hdmx);
                    removeTables.add(Tag.VDMX);
                    removeTables.add(Tag.LTSH);
                    removeTables.add(Tag.DSIG);
                    removeTables.add(Tag.vhea);
                    hintStripper.setRemoveTables(removeTables);
                    newFont = hintStripper.subset().build();
                }

                if (zipFlag) {
                    ZipOutputStream zout = new ZipOutputStream(outputStream);
                    zout.putNextEntry(new ZipEntry("simple.ttf"));
                    fontFactory.serializeFont(newFont, zout);
                    zout.closeEntry();
                    zout.close();
                } else {
                    fontFactory.serializeFont(newFont, outputStream);
                }
            }
        } finally {

            if (fis != null) {
                fis.close();
            }
        }
    }
}
